#include "estructuraAuxiliar.h"


estructuraAuxiliar estructuraAuxiliar::contadoresDeNgramas;



estructuraAuxiliar::estructuraAuxiliar(int maxNgram)
{
    nroMaximoDeNGramasPermitidos=maxNgram;
    contadorDeNGramas=0;
    ngramas=0;
    especificacion="";
    archivosRestantesAUnificar=0;
}

estructuraAuxiliar::~estructuraAuxiliar()
{
    //dtor
}

void estructuraAuxiliar::add(string ngramaComoString)
{
	std::map<string,unsigned long int*>::iterator i;
		if ((i=this->aux.find(ngramaComoString))==this->aux.end())
			{
			  //si buscando llego hasta el ultimo elemento y no es ese el que busco, inserto el nuevo ngrama
			unsigned long int* vectorDeDatos;
			if (this->especificacion!="reordenados")
			{


				switch(this->ngramas)
				{
				case 1:{
						vectorDeDatos=new unsigned long int[6];
						vectorDeDatos[1]=vectorDeDatos[2]=vectorDeDatos[3]=vectorDeDatos[4]=vectorDeDatos[5]=0;
						vectorDeDatos[0]=1;
						break;
						}
				case 2:{

						vectorDeDatos=new unsigned long int[4];
						vectorDeDatos[1]=vectorDeDatos[2]=vectorDeDatos[3]=0;
						vectorDeDatos[0]=1;
						break;
						}
				default:{
						vectorDeDatos=new unsigned long int;
						vectorDeDatos[0]=1;
						}
				}



				}
		else
			vectorDeDatos=0;



			this->aux.insert(std::pair<string,unsigned long int*>(ngramaComoString,vectorDeDatos));

			contadorDeNGramas++;


			//si se seteo un nro de ngramas maximo y se alcanzo ese maximo, dump archivo temporal.
			if (nroMaximoDeNGramasPermitidos&&(contadorDeNGramas==nroMaximoDeNGramasPermitidos))
			{
				this->dump();
			}

			}
		else //sino aumento su frecuencia.
		{
			if(this->especificacion!="reordenados")
				i->second[0]+=1;

		}


	//this->emit();  test emision
}




void estructuraAuxiliar::dump()
{

	if (this->contadorDeNGramas>0)
		{
	    std::ofstream fs(this->obtenerSiguienteNombreDeArchivo(0).c_str(),std::ios_base::out);
	    std::map <string,unsigned long int*>::iterator j;

	    for(j=aux.begin();j!=aux.end();j++){

	    	if(this->especificacion!="reordenados")
	    	{
	    	switch(this->ngramas)
	    					{
	    					case 1:{
	    							fs<< j->first <<" "<< j->second[0]<<" "<< j->second[1]<<" "<< j->second[2]<<" "<< j->second[3]<<" "<< j->second[4]<<" "<< j->second[5] << std::endl;
	    							delete[] j->second;
	    							break;
	    							}
	    					case 2:{

	    						fs<< j->first <<" "<< j->second[0]<<" "<< j->second[1]<<" "<< j->second[2]<<" "<< j->second[3] << std::endl;
	    						delete[] j->second;
	    						break;
	    							}
	    					default:{
	    						fs<< j->first <<" "<< j->second[0] << std::endl;
	    						delete[] j->second;
	    					}

	    					}
	    	}
	    	else
	    		fs<<j->first<<std::endl;

	    	aux.erase(j);
	    }

    this->contadorDeNGramas=0;
    //aux.clear();
    archivosRestantesAUnificar++;
		}
}

void estructuraAuxiliar::emit(){

	std::map <string,unsigned long int*>::iterator j;

	    for(j=aux.begin();j!=aux.end();j++){

	    	if(this->especificacion!="reordenados")
	    	    	{
	    	    	switch(this->ngramas)
	    	    					{
	    	    					case 1:{
	    	    							std::cout<< j->first <<" "<< j->second[0]<<" "<< j->second[1]<<" "<< j->second[2]<<" "<< j->second[3]<<" "<< j->second[4]<<" "<< j->second[5] << std::endl;
	    	    							break;
	    	    							}
	    	    					case 2:{
	    	    						std::cout<< j->first <<" "<< j->second[0]<<" "<< j->second[1]<<" "<< j->second[2]<<" "<< j->second[3] << std::endl;
	    	    							break;
	    	    							}
	    	    					default:std::cout<< j->first <<" "<< j->second[0] << std::endl;

	    	    					}
	    	    	}
	    	    	else
	    	    		std::cout<< j->first<<std::endl;


	    }

}


string estructuraAuxiliar::obtenerSiguienteNombreDeArchivo(int nroDeArchivoActual)
{

	//se busca el siguiente nombre de archivo con el formato specificado, hasta encontrtar el que no exista, hasta 999.



			string archivogenerico;

		   int i=nroDeArchivoActual;
			 do    {

			           	archivogenerico=obtenerNombreArchivo(i);
			            i++;

			        }
			 while(Parser::existeArchivo(archivogenerico));


	return archivogenerico;
}


void estructuraAuxiliar::unificarParDeArchivosTemporales(int nroDelPrimeroDelPar)
{
	string rutaDeArchivoUnificado= this->obtenerNombreArchivo(nroDelPrimeroDelPar/2);
	string rutaDeArchivoUnificadoTemporal=rutaDeArchivoUnificado+"_prima";
	ofstream archivoUnificadoTemporal(rutaDeArchivoUnificadoTemporal.c_str(),std::ios_base::out);

	int i=nroDelPrimeroDelPar;
	string rutaArch1="",rutaArch2="";
	bool arch1Avanza=true,arch2Avanza=true,finArch1=false,finArch2=false, ultimoMerge=(archivosRestantesAUnificar<=2);
	//unsigned long int contadorDeNgramas=0;

	Parser pArch1,pArch2;
	Tupla tuplaDe1,tuplaDe2;


	string sfreq1,sfreq2;



		rutaArch1= this->obtenerNombreArchivo(i);
		pArch1.cargarArchivo(rutaArch1.c_str());
		i++;


		rutaArch2= this->obtenerNombreArchivo(i);
		if(Parser::existeArchivo(rutaArch2)) //solo si el ultimo par consta de dos archivos existente se procede al merge.
			{
			pArch2.cargarArchivo(rutaArch2.c_str());

			while((!pArch1.archivoProcesado())&&(!pArch2.archivoProcesado())&&(rutaArch1!=rutaArch2)&&(!finArch1)&&(!finArch2))
			{
				if (arch1Avanza)
				{
				tuplaDe1 = pArch1.cargarSiguienteLinea();

				//almaceno las freq y las normalizo, para q no afecten la comparacion de tuplas.
					if(tuplaDe1.nroDeTerminos>0)
					{
						arch1Avanza=false;
					}
					else
					{
						finArch1=true;
					}

				}

				if (arch2Avanza)
				{
				tuplaDe2 = pArch2.cargarSiguienteLinea();
					if(tuplaDe2.nroDeTerminos>0)
					{
						arch2Avanza=false;
					}
					else
					{
						finArch2=true;
					}
				}
				if((finArch1!=true)||(finArch2!=true)){
				int comp= (tuplaDe1.compareTo(tuplaDe2,this->ngramas));

				if(comp<0)
				{//tupla de 2 es menor

					this->escribirTuplaCondicinada(tuplaDe2,&archivoUnificadoTemporal);
					tuplaDe2.liberarRecursos();
					//delete tuplaDe2;
					arch2Avanza=true;
				}
				else
					if (comp>0)
					{
//
						this->escribirTuplaCondicinada(tuplaDe1,&archivoUnificadoTemporal);
						tuplaDe1.liberarRecursos();
						//delete tuplaDe1;
						arch1Avanza=true;
					}
					else
					{
						this->sumarDatosDeTuplas(tuplaDe1,tuplaDe2,(tuplaDe1.nroDeTerminos-this->ngramas));

						this->escribirTuplaCondicinada(tuplaDe1,&archivoUnificadoTemporal);

						tuplaDe1.liberarRecursos();
						tuplaDe2.liberarRecursos();
						//delete tuplaDe1;
						//delete tuplaDe2;
						arch1Avanza=true;
						arch2Avanza=true;
					}
				}
			}
			}


		//por ultimo añado los elementos del archivo que no termino de ser procesado.

		if (pArch1.archivoProcesado()||(finArch1))
			{
			pArch1.eliminarArchivo();

			while((!pArch2.archivoProcesado())&&!finArch2)
					{
						Tupla tuplaTemporal=pArch2.cargarSiguienteLinea();

						this->escribirTuplaCondicinada(tuplaTemporal,&archivoUnificadoTemporal);
						tuplaTemporal.liberarRecursos();
						//delete tuplaTemporal;
					}
			pArch2.eliminarArchivo();
			archivoUnificadoTemporal.close();
			rename(rutaDeArchivoUnificadoTemporal.c_str(),rutaDeArchivoUnificado.c_str());
			}
		else
			{
			if(Parser::existeArchivo(rutaArch2))
				{
				pArch2.eliminarArchivo();


				while((!pArch1.archivoProcesado())&&!finArch1)
						{
						Tupla tuplaTemporal=pArch1.cargarSiguienteLinea();

						this->escribirTuplaCondicinada(tuplaTemporal,&archivoUnificadoTemporal);
						tuplaTemporal.liberarRecursos();
						//delete tuplaTemporal;
						}
				pArch1.eliminarArchivo();
				archivoUnificadoTemporal.close();
				rename(rutaDeArchivoUnificadoTemporal.c_str(),rutaDeArchivoUnificado.c_str());
				}
			else
				{
				archivoUnificadoTemporal.close();
				archivosRestantesAUnificar++;	//correccion alacantidad de archivos restantes para el caso de un merge de una cantidad impar de archivos.

				rename(rutaArch1.c_str(),rutaDeArchivoUnificado.c_str());
				}


			}


		archivosRestantesAUnificar--;
		//preparar archivo unificado para la siguiente etapa


		if (Parser::existeArchivo(rutaDeArchivoUnificadoTemporal))
		{
			remove(rutaDeArchivoUnificadoTemporal.c_str());
		}


}


string estructuraAuxiliar::obtenerNombreArchivo(int nroDeArchivoActual)
		{

			//se busca el siguiente nombre de archivo con el formato specificado, hasta encontrtar el que no exista, hasta 999.

			char* m=new char[1];
			sprintf (m, "%d",this->ngramas );

			string archivogenerico;

		    const int n=snprintf(NULL,0,"%d",nroDeArchivoActual);
			char buf[n+1];
			snprintf(buf,n+1,"%d",nroDeArchivoActual);
			archivogenerico= (std::string) m+"-gramas-"+especificacion+"_"+(std::string) buf;



			return archivogenerico;
		}

void estructuraAuxiliar::escribirTuplaAlArchivoUnificado(Tupla tuplaAEscribir, std::ofstream* archivoUnificado)
{
	if (archivoUnificado->is_open())
	{
		string representacion="";
		for(int i=0; i<tuplaAEscribir.nroDeTerminos;i++)
		{
			if (i!=tuplaAEscribir.nroDeTerminos-1)
			representacion+= tuplaAEscribir.tuplaDeTerminos[i]+" ";
			else
				representacion+= tuplaAEscribir.tuplaDeTerminos[i];
		}

		if(representacion!="")
		{
		*archivoUnificado<<representacion<< endl;
		}
	}


}

bool estructuraAuxiliar::unicoArchivoTemporal()
{
	string candidatoAUnico= this->obtenerNombreArchivo(0);
	if(!Parser::existeArchivo(candidatoAUnico))
	{
		cout<<"no existen archivos temporales."<<endl;
		return true;
	}
	else
	{
		return (!Parser::existeArchivo(this->obtenerNombreArchivo(1)));
	}

}

void estructuraAuxiliar::unificarArchivosTemporales()
{


	while (!this->unicoArchivoTemporal())
	{
		int i=0;

		do{
			this->unificarParDeArchivosTemporales(2*i);

			i++;

		}
		while(Parser::existeArchivo(this->obtenerNombreArchivo(i*2)));


	}

	//renombrar el unico archivo temporal.
	string nombreArchivoUnificadoFinal=this->obtenerNombreDeUnificado();
	if (Parser::existeArchivo(nombreArchivoUnificadoFinal))
		remove(nombreArchivoUnificadoFinal.c_str());

	rename(this->obtenerNombreArchivo(0).c_str(),nombreArchivoUnificadoFinal.c_str());

}

string estructuraAuxiliar::obtenerNombreDeUnificado()
{
	string tmp="";

	switch(this->ngramas)
	{
		case 1:

				tmp+="Uni";
				break;

		case 2:
				tmp+="Bi";
				break;

		case 3:
					tmp+="Tri";
					break;

		default:
				tmp+="N";
				break;
	}

	tmp+="gramas";

	if(this->especificacion!="")
	{
		tmp+="-Reordenados";
	}

	tmp+=".txt";

	return tmp;
}


unsigned long int estructuraAuxiliar::obtenerFrecuencia(string representacionDeNgrama)
{
	std::map<string,unsigned long int*>::iterator i;
			if ((i=this->aux.find(representacionDeNgrama))==this->aux.end())
				return 0;
			else
				return i->second[0];
}


void estructuraAuxiliar::escribirTuplaCondicinada(Tupla tuplaAEscribir, std::ofstream* archivoUnificado)
{

	if((this->archivosRestantesAUnificar<=2)&&(!(this->especificacion=="reordenados"))&&(!this->ngramas==0)&&(tuplaAEscribir.nroDeTerminos>0)&&(tuplaAEscribir.tuplaDeTerminos[0]!=""))
			{

				char* m=new char[1];
				sprintf (m, "%d",this->ngramas );

				estructuraAuxiliar::contadoresDeNgramas.ngramas=0;
				estructuraAuxiliar::contadoresDeNgramas.especificacion="";
				estructuraAuxiliar::contadoresDeNgramas.add((std::string)m+"-gramas");

				escribirTuplaAlArchivoUnificado(tuplaAEscribir,archivoUnificado);

				unsigned long int freq= (unsigned long int) atol(tuplaAEscribir.tuplaDeTerminos[this->ngramas].c_str());
				if (freq<4)
					{
						char* m=new char[1];
						sprintf (m, "%d",this->ngramas );
						char* n=new char[1];
						sprintf (n, "%lu",freq );
						estructuraAuxiliar::contadoresDeNgramas.add((std::string)m+"-gramas-freq-"+(std::string)n);
						delete[] m;
						delete[] n;

					}
				else
					estructuraAuxiliar::contadoresDeNgramas.add((std::string)m+"-gramas-freq-4");
				delete[] m;
			}
	else
			escribirTuplaAlArchivoUnificado(tuplaAEscribir,archivoUnificado);




}

void estructuraAuxiliar::sumarDatosDeTuplas(Tupla tuplaDe1,Tupla tuplaDe2,int elementosASumar)
{

	if(this->especificacion!="reordenados")
	{
		switch(elementosASumar)
		{
		case 6:
			{
				unsigned long int dato6DeTupla1= (unsigned long int) atol(tuplaDe1.tuplaDeTerminos[this->ngramas+5].c_str());
				unsigned long int dato6DeTupla2= (unsigned long int) atol(tuplaDe2.tuplaDeTerminos[this->ngramas+5].c_str());
				unsigned long int dato6T=dato6DeTupla1+dato6DeTupla2;

				const int n=snprintf(NULL,0,"%lu",dato6T);
				char buf1[n+1];
				snprintf(buf1,n+1,"%lu",dato6T);

				tuplaDe1.tuplaDeTerminos[this->ngramas+5]=(string)buf1;

				unsigned long int dato5DeTupla1= (unsigned long int) atol(tuplaDe1.tuplaDeTerminos[this->ngramas+4].c_str());
				unsigned long int dato5DeTupla2= (unsigned long int) atol(tuplaDe2.tuplaDeTerminos[this->ngramas+4].c_str());
				unsigned long int dato5T=dato5DeTupla1+dato5DeTupla2;


				const int m=snprintf(NULL,0,"%lu",dato5T);
				char buf2[m+1];
				snprintf(buf2,m+1,"%lu",dato5T);

				tuplaDe1.tuplaDeTerminos[this->ngramas+4]=(string)buf2;

			}
		case 4:
			{
			unsigned long int dato4DeTupla1= (unsigned long int) atol(tuplaDe1.tuplaDeTerminos[this->ngramas+3].c_str());
			unsigned long int dato4DeTupla2= (unsigned long int) atol(tuplaDe2.tuplaDeTerminos[this->ngramas+3].c_str());
			unsigned long int dato4T=dato4DeTupla1+dato4DeTupla2;

			const int n=snprintf(NULL,0,"%lu",dato4T);
			char buf1[n+1];
			snprintf(buf1,n+1,"%lu",dato4T);

			tuplaDe1.tuplaDeTerminos[this->ngramas+3]=(string)buf1;

			unsigned long int dato3DeTupla1= (unsigned long int) atol(tuplaDe1.tuplaDeTerminos[this->ngramas+2].c_str());
			unsigned long int dato3DeTupla2= (unsigned long int) atol(tuplaDe2.tuplaDeTerminos[this->ngramas+2].c_str());
			unsigned long int dato3T=dato3DeTupla1+dato3DeTupla2;


			const int m=snprintf(NULL,0,"%lu",dato3T);
			char buf2[m+1];
			snprintf(buf2,m+1,"%lu",dato3T);

			tuplaDe1.tuplaDeTerminos[this->ngramas+2]=(string)buf2;

			unsigned long int dato2DeTupla1= (unsigned long int) atol(tuplaDe1.tuplaDeTerminos[this->ngramas+1].c_str());
			unsigned long int dato2DeTupla2= (unsigned long int) atol(tuplaDe2.tuplaDeTerminos[this->ngramas+1].c_str());
			unsigned long int dato2T=dato2DeTupla1+dato2DeTupla2;


			const int k=snprintf(NULL,0,"%lu",dato2T);
			char buf3[k+1];
			snprintf(buf3,k+1,"%lu",dato2T);

			tuplaDe1.tuplaDeTerminos[this->ngramas+1]=(string)buf3;

			}
		default:{
			unsigned long int freq1= (unsigned long int) atol(tuplaDe1.tuplaDeTerminos[this->ngramas].c_str());
			unsigned long int freq2= (unsigned long int) atol(tuplaDe2.tuplaDeTerminos[this->ngramas].c_str());
			long int freqT=freq1+freq2;

			const int n=snprintf(NULL,0,"%lu",freqT);
			char buf[n+1];
			snprintf(buf,n+1,"%lu",freqT);
			tuplaDe1.tuplaDeTerminos[this->ngramas]=(string)buf;
				}


		}
	}


}
